using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace WIT.DAQ.Core.Helper;

/// <summary>
/// 动态类帮助类
/// </summary>
internal class DynamicClassHelper
{
	/// <summary>
	/// 获取属性值的方法名前缀（前缀+属性名）
	/// </summary>
	private const string Prefix_GetFieldMehotd = "GetField_";

	/// <summary>
	/// 属性赋值的方法名前缀（前缀+属性名）
	/// </summary>
	private const string Prefix_SetFieldMehotd = "SetField_";

	/// <summary>
	/// 动态创建类
	/// </summary>
	/// <param name="className">类名</param>
	/// <param name="fields">属性列表（key:属性名/value:数据类型）</param>
	/// <param name="assemblyBuilderAccess">动态程序集访问模式（默认Run/RunAndSave）</param>
	/// <returns>Type</returns>
	public static Type CreateType(string className, Dictionary<string, Type> fields, AssemblyBuilderAccess assemblyBuilderAccess = AssemblyBuilderAccess.Run)
	{
		AssemblyName assemblyName = new AssemblyName(className);
		AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, assemblyBuilderAccess);
		ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
		TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.BeforeFieldInit, null);
		ConstructorBuilder constructorBuilder = typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
		foreach (KeyValuePair<string, Type> field in fields)
		{
			CreateProperty(typeBuilder, field.Key, field.Value);
		}
		Type result = typeBuilder.CreateType();
		if (assemblyBuilderAccess == AssemblyBuilderAccess.RunAndSave || assemblyBuilderAccess == AssemblyBuilderAccess.Save)
		{
			assemblyBuilder.Save(assemblyName.Name + ".dll");
		}
		return result;
	}

	/// <summary>
	/// 创建实例
	/// </summary>
	/// <param name="type">该类型必须至少拥有1个属性</param>
	/// <returns>Instance of type</returns>
	public static object CreateInstance(Type type)
	{
		return Activator.CreateInstance(type);
	}

	/// <summary>
	/// 获取值
	/// </summary> 
	/// <param name="instance">Instance of the type.</param>
	/// <param name="propertyName">propertyName</param>
	/// <returns></returns>
	public static object GetValue(object instance, string propertyName)
	{
		Type type = instance.GetType();
		string name = "GetField_" + propertyName;
		object[] emptyTypes = Type.EmptyTypes;
		return type.InvokeMember(name, BindingFlags.InvokeMethod, null, instance, emptyTypes);
	}

	/// <summary>
	/// 获取值
	/// </summary>
	/// <param name="type">Type</param>
	/// <param name="instance">Instance of the type.</param>
	/// <param name="propertyName">propertyName</param>
	/// <returns></returns>
	public static object GetValue(Type type, object instance, string propertyName)
	{
		string name = "GetField_" + propertyName;
		object[] emptyTypes = Type.EmptyTypes;
		return type.InvokeMember(name, BindingFlags.InvokeMethod, null, instance, emptyTypes);
	}

	/// <summary>
	/// 给属性赋值
	/// </summary> 
	/// <param name="instance">Instance of the type.</param>
	/// <param name="propertyName">propertyName</param>
	/// <param name="value">value for the property，value.GetType()必须与参数数据类型完全一致，否则会报错"未找到方法Type.SetField_PropertyName"</param>
	public static void SetValue(object instance, string propertyName, object value)
	{
		instance.GetType().InvokeMember("SetField_" + propertyName, BindingFlags.InvokeMethod, null, instance, new object[1] { value });
	}

	/// <summary>
	/// 给属性赋值
	/// </summary>
	/// <param name="type">Type</param>
	/// <param name="instance">Instance of the type.</param>
	/// <param name="propertyName">propertyName</param>
	/// <param name="value">value for the property，value.GetType()必须与参数数据类型完全一致，否则会报错"未找到方法Type.SetField_PropertyName"</param>
	public static void SetValue(Type type, object instance, string propertyName, object value)
	{
		type.InvokeMember("SetField_" + propertyName, BindingFlags.InvokeMethod, null, instance, new object[1] { value });
	}

	/// <summary>
	/// 动态创建属性
	/// </summary>
	/// <param name="tb">TypeBuilder</param>
	/// <param name="propertyName">属性名称</param>
	/// <param name="propertyType">数据类型</param>
	private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
	{
		FieldBuilder field = tb.DefineField("_" + propertyName.ToLower(), propertyType, FieldAttributes.Private);
		PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
		MethodBuilder methodBuilder = tb.DefineMethod("GetField_" + propertyName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, propertyType, Type.EmptyTypes);
		ILGenerator iLGenerator = methodBuilder.GetILGenerator();
		iLGenerator.Emit(OpCodes.Ldarg_0);
		iLGenerator.Emit(OpCodes.Ldfld, field);
		iLGenerator.Emit(OpCodes.Ret);
		MethodBuilder methodBuilder2 = tb.DefineMethod("SetField_" + propertyName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, null, new Type[1] { propertyType });
		ILGenerator iLGenerator2 = methodBuilder2.GetILGenerator();
		Label loc = iLGenerator2.DefineLabel();
		iLGenerator2.MarkLabel(loc);
		iLGenerator2.Emit(OpCodes.Ldarg_0);
		iLGenerator2.Emit(OpCodes.Ldarg_1);
		iLGenerator2.Emit(OpCodes.Stfld, field);
		iLGenerator2.Emit(OpCodes.Nop);
		Label loc2 = iLGenerator2.DefineLabel();
		iLGenerator2.MarkLabel(loc2);
		iLGenerator2.Emit(OpCodes.Ret);
		propertyBuilder.SetGetMethod(methodBuilder);
		propertyBuilder.SetSetMethod(methodBuilder2);
	}
}
